package com.gws.ievent.scheduler;

import com.gws.ievent.common.enums.EventTimeTypeEnum;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 调度器
 *
 * @author song
 * @date 2022/10/17 22:14
 */
public class Trigger {
    private final static HashedWheelTimer TIMER = HashedWheelTimerInstance.INSTANCE.instance();
    private final static ThreadPoolExecutor POOL = ThreadPoolExecutorInstance.INSTANCE.instance();
    private static final Map<Long, Scheduler> I_EVENT_MAP = new ConcurrentHashMap<>(SchedulerConfig.getInstance().getMapCapacity());

    public static Trigger getInstance() {
        return TriggerInstance.INSTANCE.instance();
    }

    private Trigger() {
    }


    /**
     * 添加事件
     *
     * @param scheduler
     */
    public void add(Scheduler scheduler) {
        I_EVENT_MAP.put(scheduler.getId(), scheduler);

        try {
            // 把事件添加到时间轮
            addScheduler(scheduler);
        } catch (Exception ex) {
            // 添加失败的移除
            I_EVENT_MAP.remove(scheduler.getId());

            throw ex;
        }
    }


    /**
     * 取消事件
     *
     * @param id
     */
    public void cancel(long id) {
        if (I_EVENT_MAP.containsKey(id)) {
            Scheduler trigger = I_EVENT_MAP.remove(id);
            trigger.setCancel(true);
        }
    }


    /**
     * 添加事件
     *
     * @param scheduler
     */
    private void addScheduler(final Scheduler scheduler) {
        if (scheduler == null) {
            throw new NullPointerException("scheduler is null");
        }

        if (scheduler.getTimeType() == null) {
            throw new NullPointerException("scheduler:" + scheduler.getClass().getName() + " timeType is null");
        }

        if (scheduler.isCancel()) {
            throw new IllegalArgumentException("scheduler:" + scheduler.getClass().getName() + " is canceled");
        }

        Long delay = scheduler.delay();
        if (delay == null) {
            throw new IllegalArgumentException("scheduler:" + scheduler.getClass().getName() + " time can not parse");
        }

        // 已经过时了
        if (delay < 0L) {
            throw new IllegalArgumentException("scheduler:" + scheduler.getClass().getName() + " has expired");
        }

        // 投递任务
        postJob(scheduler);
    }


    /**
     * 投递任务
     *
     * @param scheduler
     */
    private void postJob(Scheduler scheduler) {
        if (scheduler.isCancel()) {
            return;
        }

        // 计算延时时间
        Long delay = scheduler.delay();
        if (delay.compareTo(0L) < 0) {
            return;
        }

        Timeout t = TIMER.newTimeout((timeout) -> {
            POOL.execute(() -> {
                scheduler.getJob().run(scheduler);
            });

            // 周期事件继续投递下一次
            if (scheduler.getTimeType().equals(EventTimeTypeEnum.CYCLE.getValue())) {
                postJob(scheduler);
            }
        }, delay, TimeUnit.MILLISECONDS);
    }


    private enum TriggerInstance {

        /**
         * 线程池单例
         */
        INSTANCE;
        private final Trigger trigger;

        TriggerInstance() {
            this.trigger = new Trigger();
        }

        public Trigger instance() {
            return this.trigger;
        }
    }

}
